package touchtrack.util;

import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import net.roarsoftware.lastfm.ImageSize;
import net.roarsoftware.lastfm.Track;

import org.tritonus.share.sampled.file.TAudioFileFormat;

import touchtrack.Settings;
import touchtrack.controller.ArtistController;
import touchtrack.controller.TrackController;
import touchtrack.exception.IllegalDuplicationException;
import touchtrack.exception.ItemNotFoundException;
import touchtrack.exception.NotEnoughMetaDataException;

/**
 * TrackLoader
 * 
 * Loads MP3 and Last.FM metadata into the database.
 * 
 * @todo	Deze klasse zou enkel Strings, Images, etc... moeten kunnen teruggeven.
 * 			=> onafhankelijk van de controllers maken (?niet zeker?)
 * 
 * @author	MuMe09 team 6
 * 			http://mumetech.wordpress.com/
 */
public class TrackLoader
{
    /**
     * TrackLoader constructor.
     * 
     * @param 	artistController
     * @param 	trackController
     */
    public TrackLoader(ArtistController artistController, TrackController trackController)
    {
        this.trackController = trackController;
        this.artistController = artistController;
    }
    
    /**
     * Loads a given MP3 file into our program.
     * 
     * MP3 metadata read:
     * - artist name (required)
     * - track title (required)
     * [- duration]
     * [- comment]
     * [- genre]
     * 
     * Last.FM data read:
     * - image file
     * [- summary]
     * 
     * @param 	File mp3
     * @throws 	IOException 
     * @throws 	UnsupportedAudioFileException 
     * @throws 	NotEnoughMetaDataException
     * @throws 	IllegalDuplicationException 
     */
    public void load(File mp3) 
    	throws IOException, UnsupportedAudioFileException, NotEnoughMetaDataException, IllegalDuplicationException
    {
        AudioFileFormat baseFileFormat = AudioSystem.getAudioFileFormat(mp3);
        //AudioFormat baseFormat = baseFileFormat.getFormat();
        
        // is audio file?
        if (baseFileFormat instanceof TAudioFileFormat)
        {         
        	Map properties = ((TAudioFileFormat)baseFileFormat).properties();
        	
        	// load essential metadata
        	String mp3_artistName = ((String) properties.get("author")).trim();
        	String mp3_trackTitle = ((String) properties.get("title")).trim();
        	
        	// check if enough metadata
        	if (mp3_artistName.equals("") || mp3_trackTitle.equals(""))
        		throw new NotEnoughMetaDataException(mp3_artistName, mp3_trackTitle);
        	
        	// load other metadata
        	String mp3_albumName = ((String) properties.get("album")).trim();
        	long mp3_trackDuration = (Long) properties.get("duration");  
        	String mp3_trackDate = (String) properties.get("date");
            String mp3_trackComment = (String) properties.get("comment");
            String mp3_trackGenre= (String) properties.get("mp3.id3tag.genre");
            
            // load Last.FM data
            loadLastFM(mp3_artistName, mp3_trackTitle);
            
            // get image
            Image lastFM_trackImage = null;
			try {
				lastFM_trackImage = loadTrackImage(mp3_artistName, mp3_trackTitle);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
        	
        	// call controllers
        	artistController.createArtist(mp3_artistName);
        	trackController.createLocalTrack(mp3_artistName, mp3_trackTitle, lastFM_trackImage, getSummary(mp3_trackComment), mp3.getPath()); 
        	
        	// load similar tracks
        	loadSimilarTracks(mp3_artistName, mp3_trackTitle);
        }
    }  

	/***************************************************************************
	 * 							Loading Last.FM data
	 ***************************************************************************/
    
    /**
     * @param	String artistName
     * @param	String trackTitle
     */
    private void loadLastFM(String artistName, String trackTitle)
    {
    	this.lastFM_trackInfo = net.roarsoftware.lastfm.Track.getInfo(artistName, trackTitle, Settings.LAST_FM_API_KEY);
    	this.lastFM_albumInfo = net.roarsoftware.lastfm.Album.getInfo(artistName, trackTitle, Settings.LAST_FM_API_KEY);
    	this.lastFM_artistInfo = net.roarsoftware.lastfm.Artist.getInfo(artistName, Settings.LAST_FM_API_KEY);
    }
    
    /**
     * @param 	String mp3_trackComment
     * @pre		MUST ALWAYS BE PRECEEDED BY loadLastFM(artistName, trackTitle)!!!!!!
     * @return	String summary
     */
    private String getSummary(String mp3_trackComment)
    {
    	String summary = "";
    	
    	if (this.lastFM_trackInfo != null)
    	{
    		if (this.lastFM_trackInfo.getWikiSummary() != null)
    			summary = this.lastFM_trackInfo.getWikiSummary().trim();
    	}
    	else if (mp3_trackComment != null)
    	{
    		summary = mp3_trackComment;
    	}
    	
    	return summary;
    }
    
    /**
     * @param 	String artistName
     * @param 	String trackTitle
     * @pre		MUST ALWAYS BE PRECEEDED BY loadLastFM(artistName, trackTitle)!!!!!!
     * @return	Image
     * @throws 	IOException 
     */
    private Image loadTrackImage(String artistName, String trackTitle) 
    	throws IOException 
    {
    	String image = null;
    	
    	if (this.lastFM_trackInfo != null)
    		image = lastFM_trackInfo.getImageURL(ImageSize.LARGE);
    	
    	// get image from other sources if needed
    	if (image == null || image.equals(""))
    	{
    		if (this.lastFM_albumInfo != null)
    			image = lastFM_albumInfo.getImageURL(ImageSize.LARGE);
    		
    		if (image == null || image.equals(""))
    		{
    			if (this.lastFM_artistInfo != null)
    				image = lastFM_artistInfo.getImageURL(ImageSize.LARGE);
    		}
    	}
          
    	// still no image found? use default image
    	if (image == null || image.equals(""))
    	{
    		URL imageSrc = new URL("http://geotrack.vankeer.eu/unknown.png");
    		System.out.println("[DEBUG] Track image loaded: " + imageSrc); // @DEBUG
    		return ImageIO.read(imageSrc);
    	}
    	else
    	{
    		URL imageSrc = new URL(image);
    		System.out.println("[DEBUG] Track image loaded: " + imageSrc); // @DEBUG
    		return ImageIO.read(imageSrc);
    	}
    }
    
    /**
     * @param 	String artistName
     * @param 	String trackTitle
     * @throws 	IllegalDuplicationException
     */
    public void loadSimilarTracks(String artistName, String trackTitle) 
    	throws IllegalDuplicationException
    {
		System.out.println("[DEBUG] Loading similar tracks from Last.FM: " + artistName + " - " + trackTitle);
		
		// make new hash map to store similarities
		this.similarities = new HashMap<String, LinkedList<Pair<String, Float>>>();
		
    	ArrayList<net.roarsoftware.lastfm.Track> similarTracks = 
    		(ArrayList<Track>) net.roarsoftware.lastfm.Track.getSimilar(artistName, trackTitle, null, Settings.LAST_FM_API_KEY);
    	
    	// add similarities
    	addSimilarities(getTrackDescription(artistName, trackTitle), similarTracks);
    	
    	// load external tracks
    	int i = 0;
    	for ( net.roarsoftware.lastfm.Track t : similarTracks )
    	{
    		if ( trackController.containsTrack(t.getArtist(), t.getName()) ) continue;
    		if (++i > Settings.MAX_SIMILAR_TRACKS) break;
    		
    		try {
				//loadExternalTrack(artistName + " - " + trackTitle, t);
    			loadExternalTrack(t);
			} catch (IOException e) {
				System.out.println("Can't load track image: " + getTrackDescription(artistName, trackTitle));
				e.printStackTrace();
			}
    	}
    	
    	// make connections
    	makeConnections();
    }
    
    /**
     * @param 	net.roarsoftware.lastfm.Track track
     * @throws 	IOException 
     * @throws 	IllegalDuplicationException 
     */
    private void loadExternalTrack(net.roarsoftware.lastfm.Track track) 
    	throws IOException, IllegalDuplicationException
    {
    	String artist = track.getArtist().trim();
    	String title = track.getName().trim();
    	
    	// load Last.FM data
        loadLastFM(artist, title);
        
        // get image
        Image lastFM_trackImage = loadTrackImage(artist, title);
    	
    	// call controllers
    	artistController.createArtist(artist);
    	trackController.createExternalTrack(artist, title, lastFM_trackImage, getSummary("")); 
    	
    	/* make similar tracks
    	try {
			trackController.makeSimilar(originalTrack, artist + " - " + title, track.getMatch());
		} catch (ItemNotFoundException e) {
			// should not occur...
			e.printStackTrace();
		}*/
    	
    	// add similarities
    	ArrayList<net.roarsoftware.lastfm.Track> similarTracks = 
    		(ArrayList<Track>) net.roarsoftware.lastfm.Track.getSimilar(artist, title, null, Settings.LAST_FM_API_KEY);
    	addSimilarities(getTrackDescription(artist, title), similarTracks);
    }
    
    /**
     * @param 	String track
     * @param 	ArrayList<net.roarsoftware.lastfm.Track> similarTracks
     */
    private void addSimilarities(String track, ArrayList<net.roarsoftware.lastfm.Track> similarTracks)
    {
    	LinkedList<Pair<String, Float>> newSimilarities = new LinkedList<Pair<String, Float>>();
    	
    	// check if already exist
    	if (similarities.containsKey(track))
    		newSimilarities = similarities.get(track);
    	
    	// add similarities
    	for (net.roarsoftware.lastfm.Track t : similarTracks)
    	{
    		String s = getTrackDescription(t.getArtist(), t.getName());
    		newSimilarities.add(new Pair(s, t.getMatch()));
    	}
    	
    	similarities.put(track, newSimilarities);
    }
    
    /**
     * Makes "similar track" connections in model.
     */
    private void makeConnections()
    {
    	for (String key : similarities.keySet())
    	{
    		for (Pair<String, Float> value : similarities.get(key))
    		{
    			try {
					trackController.makeSimilar(key, value.fst, value.snd);
				} catch (ItemNotFoundException e) {
					// ignore!
				}
    		}
    	}
    }
    
    /**
     * @param 	String artist
     * @param 	String title
     * @return	String artist - title
     */
    private String getTrackDescription(String artist, String title)
    {
    	return artist.trim() + " - " + title.trim();
    }
    
    private net.roarsoftware.lastfm.Track lastFM_trackInfo;
	private net.roarsoftware.lastfm.Album lastFM_albumInfo;
	private net.roarsoftware.lastfm.Artist lastFM_artistInfo;
	private HashMap<String, LinkedList<Pair<String, Float>>> similarities;

	/***************************************************************************
	 * 							Controller references
	 ***************************************************************************/

    private TrackController trackController;
    private ArtistController artistController;
}